/**
 * Copyright: Copyright (c) 2018, 2022
 * Company: 杭州叙简科技股份有限公司
 */
package cn.com.scooper.utils;

import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * HttpServlet工具类
 */
public final class HttpServletUtils {

    /**
     * 获取请求参数，先在提交参数里查找，如果没有再在cookie里面查找
     * @param request
     * @param paramName
     * @return
     */
    public static String getReqParam(HttpServletRequest request, String paramName) {
        String paramValue = request.getParameter(paramName);
        if (StringUtils.isEmpty(paramValue)) {
            paramValue = getCookie(request, paramName, null);
        }

        return paramValue;
    }

    /**
     * 获取cookie值
     * @param request
     * @param cookieName
     * @param defaultVal
     * @return
     */
    public static String getCookie(HttpServletRequest request, String cookieName, String defaultVal) {
        String value =  defaultVal;
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie: cookies) {
            if (cookieName.equalsIgnoreCase(cookie.getName())) {
                value = cookie.getValue();
                break;
            }
        }

        return value;
    }

    /**
     * 返回JSON数据
     * @param response
     * @param obj
     * @throws Exception
     */
    public static void responseJson(HttpServletResponse response, Object obj) throws Exception {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(JacksonUtils.toJSONString(obj));
        writer.close();
        response.flushBuffer();
    }

    private static final String PORXY_TYPE_NGINX = "nginx";

    /**
     * 获取真实IP地址
     * <p>如果使用了反向代理，getRemoteAddr()获取的是反向代理的IP地址，这里就必须使用其他的机制确保访问IP的正确。<p/>
     * <p>由于公司只使用了nginx反向代理服务器，所以这里只考虑nginx的情况，如果出现使用其他反向代理服务器（如：apache、weblogic等）
     * ，请按服务器的规则去解析。<p/>
     * <p>注意：千万不要将多种反向代理服务器的解析逻辑放在一起，容易被伪造IP，造成解析错误<p/>
     * @param request
     * @param proxyType 代理类型
     * @return
     */
    public static String getRealIP(HttpServletRequest request, String proxyType) {
        if (PORXY_TYPE_NGINX.equalsIgnoreCase(proxyType)) {
            getRealIPFromNginx(request);
        }

        return getRealIPFromRemoteAddr(request);
    }

    private static final String IP_PROXY_NGINX_REAL = "X-Real-IP";
    private static final String IP_PROXY_NGINX = "x-forwarded-for";
    /**
     * IP分隔符
     */
    private static final String IP_SPLIT = ",";

    /**
     * 如果请求来源于nginx，将使用nginx的规则去解析。
     * <p>nginx最外层代理服务器location必须增加如下设置：
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
     * <p/>
     * @param request
     * @return
     */
    public static String getRealIPFromNginx(HttpServletRequest request) {
        // 从Nginx中X-Real-IP获取真实ip
        String ipAddress = request.getHeader(IP_PROXY_NGINX_REAL);

        if (!isEmptyIPAddr(ipAddress)) {
            return ipAddress;
        }

        ipAddress = request.getHeader(IP_PROXY_NGINX);
        if (!isEmptyIPAddr(ipAddress)) {
            if (ipAddress.indexOf(IP_SPLIT) > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(IP_SPLIT));
            }
            return ipAddress;
        }

        return getRealIPFromRemoteAddr(request);
    }

    private static final String LOCAL_IP = "127.0.0.1";
    private static final String LOCAL_IPV6 = "0:0:0:0:0:0:0:1";
    /**
     * 从getRemoteAddr()去解析IP
     * @param request
     * @return
     */
    public static String getRealIPFromRemoteAddr(HttpServletRequest request) {
        String ipAddress = request.getRemoteAddr();
        if (LOCAL_IP.equals(ipAddress) || LOCAL_IPV6.equals(ipAddress)) {
            // 根据网卡取本机配置的IP
            InetAddress inet = null;
            try {
                inet = InetAddress.getLocalHost();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            ipAddress = inet.getHostAddress();
        }

        return ipAddress;
    }

    /**
     * 空IP地址
     * @param ipAddress
     * @return
     */
    private static boolean isEmptyIPAddr(String ipAddress) {
        return ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress);
    }

    private HttpServletUtils() {}
    
}
